/**
 *  Copyright 2013 University Pierre & Marie Curie - UMR CNRS 7606 (LIP6/MoVe)
 *  All rights reserved.   This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  Initial contributor:
 *    Jean-Luc Amitousa - Thanh Cong - <jeanluc.amitousa.mankoy@gmail.com>
 *
 *  Mailing list:
 *    lom-messan.hillah@lip6.fr
 */
package sdmakerz_v1.coresyntax.io;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.CommonPlugin;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.xtext.resource.XtextResourceSet;

import sdmakerz_v1.coresyntax.SDMakerV1RuntimeModule;
import sdmakerz_v1.coresyntax.SDMakerV1Utils;
import sdmakerz_v1.coresyntax.generator.interfaces.IInputData;
import sdmakerz_v1.coresyntax.sDMakerV1.Config;
import sdmakerz_v1.coresyntax.sDMakerV1.Diagram;
import sdmakerz_v1.coresyntax.sDMakerV1.ExternRef;
import sdmakerz_v1.coresyntax.sDMakerV1.ImportSDMData;
import sdmakerz_v1.coresyntax.sDMakerV1.InternRef;
import sdmakerz_v1.coresyntax.sDMakerV1.Reference;

public class SDMakerIO {

	private static String inputRootDirFileURI= null;

	static{
		try {
			loadRootDirNameForInput();
		} catch (IOException e) {
			inputRootDirFileURI= null;
		}
	}


	
	
	public static String getRootDirNameForInput() {
		return inputRootDirFileURI;
	}

	public static void changeRootDirNameForInput(String newRootDirName){
		inputRootDirFileURI= newRootDirName;
	}

	public static void loadRootDirNameForInput() throws IOException{
		BufferedReader br = null;
		URI uri = URI.createURI("file:///C:/Users/jean-_000/Documents/travail/informatique/upmc/stl4/sdmakerz_v1.coresyntax/src/sdmakerz_v1/coresyntax/io/rootDirNameForInput.txt");

		inputRootDirFileURI= null;
		br = new BufferedReader(new FileReader(uri.toFileString()));

		try{ inputRootDirFileURI= br.readLine(); }
		finally{ br.close(); }
	}



	/**
	 * A method for find a diagram when we only know its reference
	 * @param diagRef
	 * @return
	 * @throws IOException
	 */
	public Diagram 
	findDiagramWithImport(Reference diagRef) 
			throws IOException, 
			RefConflictException, RefNotFoundException {
		Resource input= diagRef.eResource();
		List<Resource> inputsToSearchIn= new ArrayList<Resource>();
		TreeIterator<EObject> inputContentTmp= input.getAllContents();
		String diagSimpleName= null;

		if(diagRef instanceof InternRef){
			InternRef ir= (InternRef) diagRef;
			diagSimpleName= ir.getName();
			inputsToSearchIn.add(input);
		}
		else { 
			ExternRef er= (ExternRef)diagRef;
			String packageName= 
					SDMakerV1Utils.computePackageName(er.getName());

			diagSimpleName= 
					SDMakerV1Utils.computeSimpleName(er.getName());


			if(packageName==""){

				/* This is not an absolute reference, so we have to deduct
				 * which one is the wanted.For This, we going to search the 
				 * diagram in both the current input and all imported 
				 * sdmaker data.*/

				inputsToSearchIn.add(input);

				while(inputContentTmp.hasNext()){
					EObject eo= inputContentTmp.next();

					if(eo instanceof ImportSDMData){
						inputsToSearchIn.addAll(load(((ImportSDMData)eo).getName()));
					}
				}
			}
			else{

				/* It's an absolute reference, so there is only one sdm data
				 * which contains the diagram: the one designed by the 
				 * package name */
				inputsToSearchIn.addAll(load(packageName));
			}
		}

		return findDiagram(inputsToSearchIn, diagSimpleName);
	}

	/**
	 * find a diagram, when we know resources into which to search
	 * @param inputsToSearchIn
	 * @param diagSimpleName
	 * @return a List of matching diagram. If the size is greater the 1, that's
	 * 		   an conflict error.
	 */
	public Diagram findDiagram(
			List<Resource>inputsToSearchIn, 
			String diagSimpleName)
					throws RefConflictException,
					RefNotFoundException {
		List<Diagram> matchingDiagrams= new ArrayList<Diagram>();

		for(Resource inputTmp : inputsToSearchIn){
			TreeIterator<EObject> inputContentTmp= inputTmp.getAllContents();

			while(inputContentTmp.hasNext()){
				EObject eo= inputContentTmp.next();

				if(eo instanceof Diagram){
					if(((Diagram)eo).getName()
							.compareTo(diagSimpleName)==0){
						matchingDiagrams.add((Diagram)eo);
					}
				}
			}
		}



		if (matchingDiagrams.size() == 0){
			throw new RefNotFoundException(
					"Diagram not found: "+diagSimpleName);
		}
		else if(matchingDiagrams.size() > 1){ 
			String error= 
					"conflict when searching for "+diagSimpleName+":\n"+
							"diagram found in: \n";

			for(Diagram d : matchingDiagrams){
				error += d.eResource().toString()+"\n";
			}

			throw new RefConflictException(error);
		}



		return matchingDiagrams.get(0);
	}

	//	public List<Resource> 
	//	findDiagramInputWithImport(Resource input, Reference diagRef) 
	//			throws IOException{
	//
	//		List<Resource> matchingResources= new ArrayList<Resource>();
	//		List<Resource> inputsToSearchIn= new ArrayList<Resource>();
	//		TreeIterator<EObject> inputContentTmp= input.getAllContents();
	//		String diagSimpleName= null;
	//
	//		if(diagRef instanceof InternRef){
	//			InternRef ir= (InternRef) diagRef;
	//			diagSimpleName= ir.getName();
	//			inputsToSearchIn.add(input);
	//		}
	//		else { 
	//			ExternRef er= (ExternRef)diagRef;
	//			String relativePath= 
	//					SDMakerV1Utils.computePackageName(er.getName());
	//
	//			diagSimpleName= 
	//					SDMakerV1Utils.computeSimpleName(er.getName());
	//
	//
	//
	//			if(relativePath==""){
	//
	//				inputsToSearchIn.add(input);
	//
	//				while(inputContentTmp.hasNext()){
	//					EObject eo= inputContentTmp.next();
	//
	//					if(eo instanceof ImportSDMData){
	//						inputsToSearchIn.addAll(load(((ImportSDMData)eo).getName()));
	//					}
	//				}
	//			}
	//			else{
	//				inputsToSearchIn.addAll(load(relativePath));
	//			}
	//		}
	//
	//		for(Resource inputTmp : inputsToSearchIn){
	//			inputContentTmp= inputTmp.getAllContents();
	//
	//			while(inputContentTmp.hasNext()){
	//				EObject eo= inputContentTmp.next();
	//
	//				if(eo instanceof Diagram){
	//					if(((Diagram)eo).getName()
	//							.compareTo(diagSimpleName)==0){
	//						matchingResources.add(inputTmp);
	//					}
	//				}
	//			}
	//		}
	//
	//		return matchingResources;
	//	}


	/**
	 * A method for find a config when we only know its reference
	 * @param diagRef
	 * @return
	 * @throws IOException
	 */
	public Config
	findConfigWithImports(Resource input, Reference configRef) 
			throws IOException,
			RefConflictException, RefNotFoundException{

		List<Resource> inputsToSearchIn= new ArrayList<Resource>();
		TreeIterator<EObject> inputContentTmp= input.getAllContents();
		String configSimpleName= null;

		if(configRef instanceof InternRef){
			InternRef ir= (InternRef) configRef;
			configSimpleName= ir.getName();
			inputsToSearchIn.add(input);
		}
		else{
			ExternRef er= (ExternRef)configRef;
			String packageName= 
					SDMakerV1Utils.computePackageName(er.getName());

			configSimpleName= 
					SDMakerV1Utils.computeSimpleName(er.getName());

			if(packageName==""){

				/* This is not an absolute reference, so we have to deduct
				 * which one is the wanted.For This, we going to search the 
				 * config in both the current input and all imported 
				 * sdmaker data.*/

				inputsToSearchIn.add(input);

				while(inputContentTmp.hasNext()){
					EObject eo= inputContentTmp.next();

					if(eo instanceof ImportSDMData){
						inputsToSearchIn.addAll(load(((ImportSDMData)eo).getName()));
					}
				}
			}
			else{
				/* It's an absolute reference, so there is only one sdm data
				 * which contains the config: the one designed by the 
				 * package name */

				inputsToSearchIn.addAll(load(packageName));
			}
		}

		return findConfigs(inputsToSearchIn, configSimpleName);
	}


	/**
	 * find a config, when we know resources into which to search
	 * @param inputsToSearchIn
	 * @param diagSimpleName
	 * @return a List of matching diagram. If the size is greater the 1, that's
	 * 		   an conflict error.
	 * @throws RefNotFoundException 
	 * @throws RefConflictException 
	 */
	public Config
	findConfigs(List<Resource> inputsToSearchIn, String configSimpleName) 
			throws RefNotFoundException, RefConflictException{

		List<Config> matchingConfigs= new ArrayList<Config>();

		for(Resource inputTmp : inputsToSearchIn){
			TreeIterator<EObject> inputContentTmp= inputTmp.getAllContents();

			while(inputContentTmp.hasNext()){
				EObject eo= inputContentTmp.next();

				if(eo instanceof Config){
					String confNameTmp= SDMakerV1Utils
							.computeSimpleName(((Config)eo).getName());
					if(confNameTmp.compareTo(configSimpleName)==0){
						matchingConfigs.add((Config)eo);
					}
				}
			}
		}



		if (matchingConfigs.size() == 0){
			throw new RefNotFoundException(
					"Config not found: "+configSimpleName);
		}
		else if(matchingConfigs.size() > 1){ 
			String error= 
					"conflict when searching for "+configSimpleName+":\n"+
							"config found in: \n";

			for(Config d : matchingConfigs){
				error += d.eResource().toString()+"\n";
			}

			throw new RefConflictException(error);
		}



		return matchingConfigs.get(0);
	}




	public List<IInputData> allInputs() 
			throws IOException, 
			InstantiationException, IllegalAccessException{

		URI uri= URI.createURI(inputRootDirFileURI);
		File rootDir= new File(uri.toFileString()).getCanonicalFile();

		return allInputsRec(rootDir, inputRootDirFileURI);
	}




	private List<IInputData> allInputsRec(
			File currentInputDirFile, 
			String currentInputDirFileURI) 
					throws IOException, 
					InstantiationException, 
					IllegalAccessException {



		List<IInputData> allInputs= new ArrayList<IInputData>();

		/* We will need a resSet in order to load underlying Resource for each
		 * inputData. */
		ResourceSet resSet= new ResourceSetImpl();

		IInputData inputDataTMP= null;
		String fileStrUriTMP= null;
		Resource inputTMP= null;



		for(File f : currentInputDirFile.listFiles()){
			if(f.isFile()&&f.getCanonicalPath().matches(".*\\.sdm")){

				/* Now, we going to compute the file informations needed to
				 * create an file-inputData-handler for it. */

				fileStrUriTMP= currentInputDirFileURI + "/" + f.getName();

				inputTMP= 
						resSet
						.getResource(URI.createURI(fileStrUriTMP), true);

				inputDataTMP= 
						SDMakerV1RuntimeModule
						.bindIInputData()
						.newInstance();


				inputDataTMP.setInput(inputTMP);
				allInputs.add(inputDataTMP);
			}
			else if(f.isDirectory()){
				allInputs.addAll(allInputsRec(f, currentInputDirFileURI+"/"+f.getName()));
			}
		}

		return allInputs;
	}



	/**
	 * Load Resources designed by a qualifiedName. There can be more than one
	 * Resource because the qualifiedName can be finish by a wildcard.
	 * @param qualifiedName
	 * @return
	 * @throws IOException
	 */
	private List<Resource> load(String qualifiedName) throws IOException{



		List<Resource> inputs= new ArrayList<Resource>();

		/* qualifiedName splited by "." */
		String[] splitedQN= null;

		/* If Resources are not in the root dir, we will compute the path URI
		 * That why we starting with inputRootDirFileURI which is the base for
		 * all our URIs. */
		String rootBasedStrURI= inputRootDirFileURI;

		/* We first going to compute all matching Resource URI before load them 
		 * one by one, so we will  need a list for stock them. */
		List<String> strURIs= new ArrayList<String>();

		/* In case we got a wildcard, we will need to check directory files. */
		File[] files= null;

		Resource inputTMP= null;




		if(qualifiedName.length() > 1){

			/* This is not a resource which is in the root dir */

			splitedQN= qualifiedName.split("\\.");


			for(int i= 0; i < splitedQN.length - 1; i++){
				rootBasedStrURI+= "/"+splitedQN[i];
			}

			if(splitedQN[splitedQN.length-1].compareTo("*")==0){

				files= new File(URI.createURI(rootBasedStrURI).toFileString())
				.listFiles();

				
				if(files != null){
					for (File file : files) {
						if (file.isFile()&& 
								file.getCanonicalPath().matches(".*\\.sdm")){
							strURIs.add(rootBasedStrURI+"/"+file.getName());
						}
					}
				}
			}
			else{
				/* The existence of the resource will be done when trying to
				 * load it. */
				strURIs.add(rootBasedStrURI+"/"+splitedQN[splitedQN.length-1]+".sdm");
			}




			/* Now that we found all matching Resources URIs, 
			 * we going to load them. */
			for(String strURI : strURIs){
				try{
					ResourceSet resSet= new ResourceSetImpl();
					inputTMP= resSet.getResource(URI.createURI(strURI), true);
					inputs.add(inputTMP);
				}catch(WrappedException e){}
			}
		}

		return inputs;
	}
}
